//padding around the graph and axis
var padding = 40;
var colors = ["#3399FF", "#CC3300", "#66CC66", "#FFCC00", "#C0C0C0", "#FF9999", "#CCFFFF", "#FFFFCC" ];

var Graph = Class.create({
        /* paper - The raphael paper on which to draw the graph */
        initialize: function(paper) {                
                this.paper = paper;                
        },        
        
        get_width : function(){
                return this.paper.width - padding;
        },
        get_height : function(){        
                return this.paper.height - padding;
        },
        // Returns local origin x and y
        get_origin_x : function(){
                return padding/2;
        },
        
        get_origin_y : function(){
                return padding/2;
        },        
});


/* Pie graph*/


var PieGraph = Class.create(Graph, {

        /*
         * data - Represents the slices of the pie chart.
         *        2 columns of the Label and Value data
         *        and rows of the form: ['Label', Value]
         *        where "Label" labels the given slice of Value.         
         */
        _draw_border : function(){
                // Draw border
                var border = this.paper.rect(0,0, this.paper.width-1, this.paper.height-1);
                border.attr({stroke : "gray"});
        },
        
        _draw_guides : function(radius, cx, cy){
                this.paper.rect(cx,cy, this.get_width()/2, this.get_height()/2);
                this.paper.rect(this.get_origin_x(),
                        this.get_origin_y(), this.get_width()/2, this.get_height()/2);
                this.paper.rect(cx-radius,cy-radius, 
                        radius*2, radius*2);                        
        },

        
        _draw_legend : function(data, options){
        
                if(options.show_legend == false){ return;}
                
                var legend_width = 90;
                var legend_height = 80;
                var text_height = 20;
                var legend_padding = 10;
                var legend_icon_size = 10;
                
                var start_x = this.get_width() + this.get_origin_x() - legend_width;                
                var start_y = this.get_height() + this.get_origin_y() - legend_height           
                
                /*
                var r = this.paper.local.rect( start_x,
                        start_y,
                        legend_width,
                        legend_height);
                r.attr({'stroke' : 'gray'});
                */
                var curr_x = start_x + legend_padding;
                var curr_y = start_y + legend_height - legend_padding;
                
                for(var i=0; i<data.num_rows(); i++){
                
                        // Draw the little colored square
                        var icon = this.paper.local.rect(curr_x , curr_y- legend_icon_size/2, legend_icon_size, legend_icon_size);
                        icon.attr({'fill' : colors[i%colors.length],
                                'stroke' : 'gray'});
                        
                        var wx = this.paper.local.l2w_x(curr_x + legend_icon_size*2);
                        var wy = this.paper.local.l2w_y(curr_y);
                        
                        var label = this.paper.text(wx, wy, data.get_cell(i, 0));
                        label.attr("text-anchor","start"); 
                        //label.attr({'fill' : 'gray'});
                        curr_y -= text_height;
                }
        },
        
        /*
        * options:
                show_legend - If false does not show the legend
        */
        draw : function(data, options){
                /* data = DataTable(columns=["Label", "Value"])
                        data.rows =[ ['healthy', 10], ['unhealthy' , 20]...
                */
                
                if(options == null){ options = {} };
                
                this._draw_border();
                this._draw_graph(data, options);
                this._draw_legend(data, options);
                
                
        },
        
        _draw_graph : function(data, options){
                var total = 0;
                // Calculate total                
                for(var i=0; i < data.num_rows(); i++){
                        var row = data.get_row(i);                        
                        var value = row[1];                        
                        total += value;
                }

                // Center of the canvas
                var cx = this.get_width()/2 + this.get_origin_x();
                var cy = this.get_height()/2 + this.get_origin_y();
                
                        
        
                /* End draw guiding scquares */
                
                // Arc properties
                var radius = Math.min(this.get_width(), this.get_height()) / 2;
                var x_axis_rotation = 0;
                var large_arc_flag = 0;
                var sweep_flag = 0;
                
                /* Draw guiding squares - DEBUGGIN */                        
                //this._draw_guides(radius, cx, cy);
                
                        
                var last_point_x = cx + radius;
                var last_point_y = cy;
                        
                var total_so_far = 0;
                
                
                for(var i=0; i < data.num_rows(); i++){
                        var row = data.get_row(i);
                        var label = row[0];                        
                        var value = row[1];
                        
                        total_so_far += value;
                        
                        // What is the theta angle for this slice
                        var theta = ( total_so_far * (Math.PI * 2)) / total;
                        //theta = theta - spacing_between_slices;
                        //theta += Math.PI/4; // Start from the vertical line
                        
                        // Calculate where to terminate the curve in cartesian
                        var to_x = radius * Math.cos(theta) + cx;
                        var to_y = radius * Math.sin(theta) + cy;
                        
                        wto_y = this.paper.local.l2w_y(to_y);
                        wto_x = this.paper.local.l2w_x(to_x);
                        
                        wlast_point_x = this.paper.local.l2w_x(last_point_x);
                        wlast_point_y = this.paper.local.l2w_y(last_point_y);
                                                
                        /* Draw a slice
                        * 1. Move to the next free point on circle calculated by last iteration or
                        *    directly above center if this is first iteration.
                        * 2. Draw an arc to the point calculated from this data slice
                        * 3. Draw line to center
                        * 4. Close path
                        */
                        
                        //this.paper.circle(wlast_point_x, wlast_point_y, 2);
                        //this.paper.circle(wto_x, wto_y, 2);
                        
                        var slice_str = ("M" + wlast_point_x + " " + wlast_point_y 
                                + "A"                                
                                + radius + " "
                                + radius + " "
                                + x_axis_rotation+" "
                                + large_arc_flag +" " 
                                + sweep_flag + " "
                                + wto_x + " "
                                + wto_y + " "
                                + " L" 
                                + cx + " " 
                                + cy + " "
                                + "z");
                                
                                
                        var slice = this.paper.path(slice_str);                        
                        slice.attr({'fill' : colors[i % colors.length]});
                        slice.attr({'stroke' : colors[i%colors.length]});
                        // Remmember the destination as the source for next iteration                        
                        last_point_x = to_x;
                        last_point_y = to_y;
                        

                        /* Interactivity for the graph */
                        slice.node.onmouseover = function(slice, evt){                                
                                slice.toFront();
                                slice.attr({'stroke':'white', 'stroke-width':3});
                        }.curry(slice);
                        
                        slice.node.onmouseout = function(slice, default_color, evt){
                                slice.attr({'stroke':default_color, 'stroke-width':1});
                                
                        }.curry(slice, colors[i%colors.length]);
                                
                        
                }
        },
        
});

/* Line graph  */


var dot_radius = 4;
var dot_fill = "white";
var dot_stroke_color = "gray";
var default_num_ticks = 10;



var LineGraph = Class.create(Graph, {


        _draw_x_axis : function(){
                //Draw horizontal axis
                this.paper.path("M"+ this.get_origin_x()+ " " + this.paper.local.l2w_y(this.get_origin_y())
                                + "L" + (this.get_origin_x() + this.get_width()) + " " 
                                + this.paper.local.l2w_y(this.get_origin_y()) );
        },
        
        _draw_y_axis : function(){
                this.paper.path("M"+ this.get_origin_x()+ " " + this.get_origin_y()
                                + "L" + this.get_origin_x() + " " 
                                + (this.get_origin_y() + this.get_height())
                                );
        },
        
        _draw_x_axis_ticks : function(x_ticks, max_x, min_x){
                for(var i=0; i<x_ticks.length; i++){
                        var x_tick = x_ticks[i];

                        // Calculate x
                        var normalized_x = x_tick - min_x;                                
                        if(max_x - min_x < 0.0001){                        
                                var x =  this.get_origin_x();
                        }else{
                                var x = (this.get_width() * normalized_x)/(max_x - min_x) +  this.get_origin_x();                                
                        }
                        
                        if(x_tick >= min_x && x_tick <= max_x){
                                var wx = this.paper.local.l2w_x(x);
                                var wy = this.paper.local.l2w_y(this.get_origin_y());
                                
                                this.paper.path("M"+wx +" "+(wy-3) + "l0 6");
                                this.paper.text(wx, wy+10, x_tick);
                        }
                }
        },
        
        _draw_y_axis_ticks : function(y_ticks, max_y, min_y){
                for(var i=0; i<y_ticks.length; i++){                        
                        var y_tick = y_ticks[i];
                        var y = (this.get_height() * y_tick) / max_y + this.get_origin_y();
                        var wx = this.paper.local.l2w_x(this.get_origin_x());
                        var wy = this.paper.local.l2w_y(y);
                        this.paper.path("M"+(wx-2) + " "+ wy + "l4 0");
                        this.paper.text(wx-10, wy, y_tick);
                }
        },

        _generate_x_ticks : function(data, max_x, min_x){
                var x_ticks = [];        
                for(i=0; i<default_num_ticks; i++){
                        var xtick_amount = (max_x - min_x)/(default_num_ticks-1);                        
                        x_ticks.push( i*xtick_amount + min_x);                        
                        
                }
                return x_ticks;
        },
        
        _generate_y_ticks : function(data, max_y, min_y){
                var y_ticks = [];
                for(i=0; i<default_num_ticks; i++){                        
                        var ytick_amount = (max_y)/(default_num_ticks-1);
                        var y_rounded = Math.round(i*ytick_amount*100)/100;                        
                        y_ticks.push( y_rounded);
                }
                return y_ticks;
        },
        
        _draw_border : function(){
                // Draw border
                var border = this.paper.rect(0,0, this.paper.width-1, this.paper.height-1);
                border.attr({stroke : "gray"});
        },
        _draw_axis : function(data, show_axis, ticks_x, ticks_y){        
                // If the axis is not to be drawn - just return
                if(show_axis == false)
                        return;
                        
                this._draw_x_axis();
                this._draw_y_axis();
                
                var max_y = this.get_max_y(data);
                var min_y = 0;
                var min_x = data.get_col_min(0);
                var max_x = data.get_col_max(0);
                
                //Draw axis ticks                
                if(ticks_x == null){
                        var x_ticks = this._generate_x_ticks(data, max_x, min_x);
                }else{
                        
                        var x_ticks = ticks_x;
                }
                
                if(ticks_y == null){
                        var y_ticks = this._generate_y_ticks(data, max_y, min_y);
                }else{
                        var y_ticks = ticks_y;
                }                
                                
                this._draw_x_axis_ticks(x_ticks, max_x, min_x);
                this._draw_y_axis_ticks(y_ticks, max_y, 0);
                
                
        },
        
        _draw_graph : function(data, options){
                //TODO - this is not cached
                var max_y = this.get_max_y(data);
                var min_x = data.get_col_min(0);
                var max_x = data.get_col_max(0);
                
                //Iterate over columns
                for(var i=1; i < data.num_cols(); i++){
                        var line_str = null;
                        
                        // Iterate over all the rows - building up the path string
                        for(var j=0; j<data.num_rows(); j++){
                                
                                //All the data for a given column
                                var y_value = data.get_cell(j, i);
                                
                                var y = (this.get_height() * y_value) / max_y + this.get_origin_y();
                                                                
                                var x_value = data.get_cell(j, 0);                                
                                
                                var normalized_x = x_value - min_x;
                                
                                if(max_x - min_x < 0.0001){
                                        var x =  this.get_origin_x();
                                }else{
                                        var x = (this.get_width() * normalized_x)/(max_x - min_x) +  this.get_origin_x();
                                }
                               
                                var wx = this.paper.local.l2w_x(x);
                                var wy = this.paper.local.l2w_y(y);
                                
                                if(line_str == null){
                                        line_str = "M" + wx + " " + wy;
                                }else{
                                        line_str = line_str + "L" + wx + " " + wy;
                                        //line_str = line_str + "S" + (wx-41) + " " + (wy+12) + " "+wx + " " + wy;
                                }

                                //Draw the dot                                
                                var dot = this.paper.circle(wx, wy, dot_radius);
                                dot.attr({fill : dot_fill, stroke : dot_stroke_color });                              
                                
                                
                                /* Interactivity */
                                dot.node.onmouseover = function(shape, evt){                                        
                                        shape.attr({'r': dot_radius + 2});
                                        shape.attr({'stroke' : 'black', 'stroke-width':2});
                                        shape.toFront();
                                        
                                        
                                }.curry(dot);
                                
                                dot.node.onmouseout = function(shape, evt){
                                        shape.attr({'r' : dot_radius, 'stroke-width' : 1, 'stroke':dot_stroke_color});
                                        
                                }.curry(dot);
                                
                        }
                        if( data.num_rows() > 1){
                                var l = this.paper.path(line_str);
                                
                                l.attr({stroke: colors[i-1]});
                                // Make sure it's behind the dots
                                l.toBack();
                                
                        }
                        
                }            
        
        },
        
        _draw_legend : function(data, options){
                
                
                
        },
        
        
        
        /*
        * data - this is a table table with rows of data.
        *       Each row represents a line on a line graph with the
        *       first row-value representing the x-value of the row.
        *       So a row of the form: [x, y1, y2, y3, y4..]
        *       represents points (x,y1), (x,y2), (x, y3)  and so on.
        *        
        * options:
        *       x_ticks - array specifying the ticks on x axis
        *       y_ticks - array specifying the ticks on y axis
        *       show_axis - If set to false - hide the axis
        */
        draw : function(data, options){
        
                if(options == null){ options = {}; }

                // Draw the border around the graph
                this._draw_border();
                
                // Draw the axis for the graph
                this._draw_axis(data, options['show_axis'], options['x_ticks'], options['y_ticks']);
                
                this._draw_graph(data, options);
                this._draw_legend(data, options);                
        },
        
        get_max_y : function(data){
                var max_value = 0;
                for(var i=0; i<data.num_cols(); i++){                
                
                        if(i == 0){continue;}
                        var val = data.get_col_max(i);
                        
                        if(val > max_value){
                                max_value = val;
                        }                                
                }                
                return max_value;
        },
        
});



/* Bar Graph */


var text_height = 5;

//Padding between groups of bars
var bar_group_padding = 8;
var bar_stroke_color = 'none';
//var colors = ["#FFFF00", "#0066FF", "#CC66FF" ];


var BarGraph = Class.create(Graph, {

        get_max_y :function(data){
                var max_value = 0;
                for(var col_index=0; col_index < data.num_cols(); col_index++){
                        if(col_index == 0){ continue; }
                        
                        var curr_max =  data.get_col_max(col_index);
                        if (curr_max > max_value){
                                max_value = curr_max;
                        }
                }
                return max_value;
        },
        
       
        /*
        * data - table where:
        *       First column represents the x-criteria
        *       Other columns represent the different types of
        *       y-values to be plotted.
        *
        *       So:
        *       [criteria_A, bar1_value, bar2_value ]
        *       [criteria_B, bar1_value, bar2_value ]
        * Produces a bar graph with 4 bars in total, with 2 in A and 2 in B.
        *
        * options:
        *       chubby - if true produces a "chubby" bar graph
        */
        draw : function(data, options){                
                if (options == null){ options = {}; }
                
                //Calculate the dimentions of data
                //to autosize the graph
                var num_groups = data.num_rows();
                var data_columns = data.num_cols() - 1;
                var num_bars = num_groups * data_columns;
                                
                var max_value = this.get_max_y(data);
                
                //See how wide to make the bars
                var space_avail = this.get_width() - (num_groups - 1)* bar_group_padding;
                var bar_width = space_avail * 1.0/ num_bars;
                                
                //draw borders                
                var border = this.paper.local.rect(0,0, this.paper.width-1, this.paper.height-1);
                border.attr({stroke : 'gray'});
                
                for(var i=0; i< data.num_rows(); i++){
                        
                        // Draw bar
                        var row = data.get_row(i);
                        
                        for(var j=0; j < data_columns; j++){
                                var bar_height = (this.get_height() * row[j+1] * 1.0) / max_value;
                                var x = i * bar_width * data_columns + j*bar_width + this.get_origin_x() + i * bar_group_padding;
                                
                                var y = this.get_origin_y();
                                
                                
                                var wx = this.paper.local.l2w_x(x);
                                var wy = this.paper.local.l2w_y(y);
                                
                                var shape = null;
                                if(options.chubby == true){
                                        var path_str = ( "M" + wx + " " + wy +"S" + (wx-4) + " " + (wy-bar_height/2) + " " + wx + " " + (wy-bar_height)
                                        +"L" + (wx+bar_width) + " " + (wy-bar_height)
                                        +"S" + (wx+bar_width+4) + " " + (wy-bar_height/2) + " " + (wx+bar_width) + " " + (wy)
                                        +"z");
                                        shape = this.paper.path(path_str);
                                        shape.attr({fill:colors[j]});
                                }else{
                                        shape = this.paper.local.rect(x, y, bar_width, bar_height - 1);        
                                        shape.attr({fill:colors[j], stroke:bar_stroke_color});                                        
                                }
                                
                                shape.node.onmouseover = function(shape, evt){                                        
                                        shape.attr({'stroke':'black', 'stroke-width':3});
                                        shape.toFront();
                                }.curry(shape);
                                shape.node.onmouseout = function(shape, evt){
                                        shape.attr({'stroke':'gray', 'stroke-width':1});
                                }.curry(shape);
                                
                                var text = this.paper.local.text(x+bar_width/2, y + bar_height+5, row[j+1]);
                                text.attr({fill : 'gray'});
                                
                        }
                        
                        //Draw label under each group of bars
                        var label = this.paper.local.text( i*bar_width*data_columns + bar_width*data_columns/2 
                                + padding/2
                                + bar_group_padding*i,
                                padding/6, 
                                row[0]);
                        
                        label.attr({"font-size" : "11"});                        
                }
        },
        
        
});